﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Transactions;
using Sucdri.Domain.Interface;
using Sucdri.Domain.Model;
using Sucdri.Domain.Repository.DataMapper;

namespace Sucdri.Domain.Repository
{
   public class UnitOfWork:IUnitOfWork
   {
      private readonly IList<DomainEntity> _newObject = new List<DomainEntity>();
      private readonly IList<DomainEntity> _dirtyObject = new List<DomainEntity>();
      private readonly IList<DomainEntity> _removedObject = new List<DomainEntity>();

      private const string ContextKey = "Sucdri.Domain.Repository.UnitOfWork";

      public static IUnitOfWork Current
      {
         get { return CallContext.GetData(ContextKey) as IUnitOfWork; }
      }

/*      private readonly IList<ScheduledAction> _scheduledActions;

      internal IList<ScheduledAction> ScheduledActions
      {
         get { return _scheduledActions; }
      }*/

/*
      public UnitOfWork()
      {
         _scheduledActions = new List<ScheduledAction>();
      }
*/

      public static IUnitOfWork BeginTransaction()
      {
         if (CallContext.GetData(ContextKey) != null)
            throw new InvalidOperationException("Unit Of Work has already begin");
         IUnitOfWork uow = new UnitOfWork();
         CallContext.SetData(ContextKey, uow);
         return uow;
      }

      public void Commit()
      {
         var uow = CallContext.GetData(ContextKey) as IUnitOfWork;
         if (uow == null) return;
         using (var transScope = new TransactionScope())
         {
            InsertNew();
            UpdateDirty();
            DeleteRemoved();
            transScope.Complete();
         }
         ClearAll();
      }

      public void RegisterNew(DomainEntity domainEntity)
      {
         if(_newObject.Contains(domainEntity))
            throw new ArgumentException("");
         if (_dirtyObject.Contains(domainEntity))
            throw new ArgumentException("");
         if (_removedObject.Contains(domainEntity))
            throw new ArgumentException("");
         _newObject.Add(domainEntity);
      }

      public void RegisterDirty(DomainEntity domainEntity)
      {
         if (_removedObject.Contains(domainEntity))
            throw new ArgumentException("");
         if (_newObject.Contains(domainEntity))
         {
            _newObject.Remove(domainEntity);
            _newObject.Add(domainEntity);
         }
         if (_dirtyObject.Contains(domainEntity))
         {
            _dirtyObject.Remove(domainEntity);
            _dirtyObject.Add(domainEntity);
         }
         _dirtyObject.Add(domainEntity);
      }

      public void RegisterRemoved(DomainEntity domainEntity)
      {
         if (_newObject.Contains(domainEntity))
            _newObject.Remove(domainEntity);
         if (_dirtyObject.Contains(domainEntity))
            _dirtyObject.Remove(domainEntity);
         if (!_removedObject.Contains(domainEntity))
            _newObject.Add(domainEntity);
      }

      private void InsertNew()
      {
         foreach (var domainEntity in _newObject)
         {
            IDataMapper mapper = MapperRegistry.GetMapper(domainEntity.GetType().FullName);
            if (mapper==null)
               throw new InvalidOperationException("DataMapper is null");
            mapper.Create(domainEntity);
         }
      }

      private void UpdateDirty()
      {
         foreach (var domainEntity in _newObject)
         {
            IDataMapper mapper = MapperRegistry.GetMapper(domainEntity.GetType().FullName);
            mapper.Update(domainEntity);
         }
      }

      private void DeleteRemoved()
      {
         foreach (var domainEntity in _newObject)
         {
            IDataMapper mapper = MapperRegistry.GetMapper(domainEntity.GetType().FullName);
            mapper.Delete(domainEntity);
         }
      }

      public void RollBack()
      {
         ClearAll();
      }

      private void ClearAll()
      {
         _newObject.Clear();
         _dirtyObject.Clear();
         _removedObject.Clear();
      }

      public void Dispose()
      {
         ClearAll();
         //throw new NotImplementedException();
      }
   }
}
